package ge.telasi.tasks.ui.comp;

import ge.telasi.tasks.model.Structurable;
import java.util.List;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

/**
 * @author dimitri
 */
public abstract class StructurableTreeModel<T extends Structurable> extends DefaultTreeModel {
    private List<T> data;

    public StructurableTreeModel(List<T> list, String rootName) {
        super(createRootNode(list, rootName));
        this.data = list;
    }

    static DefaultMutableTreeNode createRootNode(List<? extends Structurable> list, String rootName) {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode(rootName);
        if (list != null) {
            for (Structurable str : list) {
                append(root, str);
            }
        }
        return root;
    }

    static void append(DefaultMutableTreeNode parent, Structurable child) {
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(child);
        parent.add(newNode);
        List<Structurable> list = child.getChildren();
        if (list != null) {
            for (Structurable structurable : list) {
                append(newNode, structurable);
            }
        }
    }

    public void insert(T structurable) {
        T parent = (T) structurable.getParent();
        DefaultMutableTreeNode parentNode = null;
        if (parent != null) {
            parentNode = findNodeFor(parent);
        }
        if (parentNode == null) {
            parentNode = (DefaultMutableTreeNode) getRoot();
        }
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(structurable);
        insertNodeInto(newNode, parentNode, parentNode.getChildCount());
    }

    public void replace(T old, T newStructure) {
        DefaultMutableTreeNode oldNode = findNodeFor(old);
        if (oldNode != null) {
            removeNodeFromParent(oldNode);
            oldNode.setUserObject(newStructure);
            DefaultMutableTreeNode parentNode = findNodeFor(newStructure.getParent());
            if (parentNode == null) {
                parentNode = (DefaultMutableTreeNode) getRoot();
            }
            insertNodeInto(oldNode, parentNode, parentNode.getChildCount());
        }
    }

    public void remove(T structurable) {
        DefaultMutableTreeNode node = findNodeFor(structurable);
        if (node != null) {
            removeNodeFromParent(node);
        }
    }

    public TreePath path(T structurable) {
        DefaultMutableTreeNode node = findNodeFor(structurable);
        TreeNode[] nodes = getPathToRoot(node);
        if (node != null) {
            return new TreePath(nodes);
        }
        return null;
    }

    private DefaultMutableTreeNode findNodeFor(Object object) {
        DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) getRoot();
        return findNode(rootNode, object);
    }

    private DefaultMutableTreeNode findNode(DefaultMutableTreeNode parent, Object find) {
        for (int i = 0; i < parent.getChildCount(); i++) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) parent.getChildAt(i);
            Object object = node.getUserObject();
            if (object != null && object.equals(find)) {
                return node;
            }
            DefaultMutableTreeNode lookup = findNode(node, find);
            if (lookup != null) {
                return lookup;
            }
        }
        return null;
    }

}
